package com.pickgliss.ui.controls.list
{
	import com.pickgliss.events.InteractiveEvent;
	import com.pickgliss.events.ListItemEvent;
	import com.pickgliss.geom.IntDimension;
	import com.pickgliss.geom.IntPoint;
	import com.pickgliss.geom.IntRectangle;
	import com.pickgliss.ui.ComponentSetting;
	import com.pickgliss.ui.controls.cell.IListCell;
	import com.pickgliss.ui.controls.cell.IListCellFactory;
	import com.pickgliss.ui.core.Component;
	import com.pickgliss.ui.core.IViewprot;
	import com.pickgliss.utils.ObjectUtils;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.MouseEvent;

	[Event(name="listItemClick",type="com.pickgliss.events.ListItemEvent")]
	[Event(name="listItemDoubleclick",type="com.pickgliss.events.ListItemEvent")]
	[Event(name="listItemMouseDown",type="com.pickgliss.events.ListItemEvent")]
	[Event(name="listItemMouseUp",type="com.pickgliss.events.ListItemEvent")]
	[Event(name="listItemRollOver",type="com.pickgliss.events.ListItemEvent")]
	[Event(name="listItemRollOut",type="com.pickgliss.events.ListItemEvent")]
	public class List extends Component implements IViewprot,ListDataListener
	{
		public static const AUTO_INCREMENT:int = int.MIN_VALUE;
		
		public static const P_cellFactory:String = "cellFactory";
		public static const P_horizontalBlockIncrement:String = "horizontalBlockIncrement";
		public static const P_horizontalUnitIncrement:String = "horizontalUnitIncrement";
		public static const P_model:String = "model";
		public static const P_verticalBlockIncrement:String = "verticalBlockIncrement";
		public static const P_verticalUnitIncrement:String = "verticalUnitIncrement";
		public static const P_viewPosition:String = "viewPosition";
		public static const P_viewSize:String = "viewSize";
		
		public function List(){}
		
		protected var _cells:Vector.<IListCell>;
		protected var _cellsContainer:Sprite;
		protected var _factory:IListCellFactory
		protected var _firstVisibleIndex:int;
		protected var _firstVisibleIndexOffset:int;
		protected var _horizontalBlockIncrement:int = ComponentSetting.SCROLL_BLOCK_INCREMENT;
		protected var _horizontalUnitIncrement:int = ComponentSetting.SCROLL_UINT_INCREMENT;
		protected var _lastVisibleIndex:int;
		protected var _lastVisibleIndexOffset:int;
		protected var _maskShape:Shape;
		protected var _model:IListModel;
		protected var _mouseActiveObjectShape:Shape;
		protected var _verticalBlockIncrement:int = ComponentSetting.SCROLL_BLOCK_INCREMENT;
		protected var _verticalUnitIncrement:int = ComponentSetting.SCROLL_UINT_INCREMENT;
		protected var _viewHeight:int;
		protected var _viewPosition:IntPoint;
		protected var _viewWidth:int;
		protected var _viewWidthNoCount:int;
		protected var _visibleCellWidth:int;
		protected var _visibleRowCount:int;
		
		public function addStateListener(listener:Function, priority:int=0, useWeakReference:Boolean=false):void
		{
			addEventListener(InteractiveEvent.STATE_CHANGED, listener, false, priority);
		}
		
		public function get cellFactory():IListCellFactory
		{
			return _factory;
		}
		public function set cellFactory(factory:IListCellFactory):void
		{
			if(_factory == factory) return;
			_factory = factory;
			onPropertiesChanged(P_cellFactory);
		}
		
		public function contentsChanged(event:ListDataEvent):void
		{
			if((event.getIndex0() >= _firstVisibleIndex && event.getIndex0() <= _lastVisibleIndex)
				|| event.getIndex1() >= _firstVisibleIndex && event.getIndex1() <= _lastVisibleIndex
				|| _lastVisibleIndex == -1)
			{
				updateListView();
			}
		}
		
		override public function dispose():void
		{
			_mouseActiveObjectShape.graphics.clear();
			_mouseActiveObjectShape = null;
			_maskShape.graphics.clear();
			_maskShape = null;
			removeAllCell();
			_cells = null;
			if(_model)_model.removeListDataListener(this);
			_model = null;
			super.dispose();
		}
		
		public function getExtentSize():IntDimension
		{
			return new IntDimension(_width,_height);
		}
		
		public function getViewSize():IntDimension
		{
			return new IntDimension(_viewWidth,_viewHeight);
		}
		
		public function getViewportPane():Component
		{
			return this;
		}
		
		public function get horizontalBlockIncrement():int
		{
			return _horizontalBlockIncrement;
		}
		
		public function set horizontalBlockIncrement(increment:int):void
		{
			if(_horizontalBlockIncrement == increment)return;
			_horizontalBlockIncrement = increment;
			onPropertiesChanged(P_horizontalBlockIncrement);
		}
		
		public function get horizontalUnitIncrement():int
		{
			return _horizontalUnitIncrement;
		}
		
		public function set horizontalUnitIncrement(increment:int):void
		{
			if(_horizontalUnitIncrement == increment)return;
			_horizontalUnitIncrement = increment;
			onPropertiesChanged(P_horizontalUnitIncrement);
		}
		
		public function intervalAdded(event:ListDataEvent):void
		{
			refreshViewSize();
			onPropertiesChanged(P_viewSize);
			if(event.getIndex1() <= _lastVisibleIndex || _lastVisibleIndex == -1 || _viewHeight < _height)
			{
				updateListView();
			}
		}
		
		public function intervalRemoved(event:ListDataEvent):void
		{
			refreshViewSize();
			onPropertiesChanged(P_viewSize);
			if(event.getIndex1() <= _lastVisibleIndex || _lastVisibleIndex == -1 || _viewHeight < _height)
			{
				updateListView();
			}
		}
		
		public function isSelectedIndex(index:int):Boolean
		{
			return _currentSelectedIndex == index;
		}
		
		
		public function get model():IListModel{
			return _model;
		}
		public function set model(m:IListModel):void
		{
			if(m != model)
			{
				if(_model)_model.removeListDataListener(this);
				_model = m;
				_model.addListDataListener(this);
				onPropertiesChanged(P_model);
			}
		}
		
		public function removeStateListener(listener:Function):void
		{
			removeEventListener(InteractiveEvent.STATE_CHANGED, listener);
		}
		
		public function scrollRectToVisible(contentRect:IntRectangle):void
		{
			viewPosition = new IntPoint(contentRect.x,contentRect.y);
		}
		
		public function scrollToIndex(val:int):void
		{
			if(val<0 || val>=model.getSize()) return;
			var resultPos:IntPoint = new IntPoint();
			resultPos.x = 0;
			resultPos.y = val * _factory.getCellHeight();
			viewPosition = resultPos;
		}
		
		public function setListData(ld:Array):void{
			var m:IListModel = new VectorListModel(ld);
			model = m;
		}
		
		public function setViewportTestSize(s:IntDimension):void
		{
			
		}
		
		public function updateListView():void
		{
			if(_factory == null)return;
			createCells();
			updateShowMask();
			updatePos();
		}
		
		public function get verticalBlockIncrement():int
		{
			return _verticalBlockIncrement;
		}
		
		public function set verticalBlockIncrement(increment:int):void
		{
			if(_verticalBlockIncrement == increment)return;
			_verticalBlockIncrement = increment;
			onPropertiesChanged(P_verticalBlockIncrement);
		}
		
		public function get verticalUnitIncrement():int
		{
			return _verticalUnitIncrement;
		}
		
		public function set verticalUnitIncrement(increment:int):void
		{
			if(_verticalUnitIncrement == increment) return;
			_verticalUnitIncrement = increment;
			onPropertiesChanged(P_verticalUnitIncrement);
		}
		
		public function get viewPosition():IntPoint
		{
			return _viewPosition;
		}
		public function set viewPosition(p:IntPoint):void
		{
			if(_viewPosition.equals(restrictionViewPos(p)))return;
			_viewPosition.setLocation(p);
			onPropertiesChanged(P_viewPosition);
		}
		
		protected function __onItemInteractive(event:MouseEvent):void
		{
			var target:IListCell = event.currentTarget as IListCell;
			var index:int = _model.indexOf(target.getCellValue());
			var type:String;
			switch(event.type)
			{
				case MouseEvent.CLICK:
					type = ListItemEvent.LIST_ITEM_CLICK;
					_currentSelectedIndex = index;
					updateListView();
					break;
				case MouseEvent.DOUBLE_CLICK:
					type = ListItemEvent.LIST_ITEM_DOUBLE_CLICK;
					break;
				case MouseEvent.MOUSE_DOWN:
					type = ListItemEvent.LIST_ITEM_MOUSE_DOWN;
					break;
				case MouseEvent.MOUSE_UP:
					type = ListItemEvent.LIST_ITEM_MOUSE_UP;
					break;
				case MouseEvent.ROLL_OVER:
					type = ListItemEvent.LIST_ITEM_ROLL_OVER;
					break;
				case MouseEvent.ROLL_OUT:
					type = ListItemEvent.LIST_ITEM_ROLL_OUT;
					break;
			}
			dispatchEvent(new ListItemEvent(target,target.getCellValue(),type,index));
		}
		
		protected var _currentSelectedIndex:int = -1;
		public function get currentSelectedIndex():int
		{
			return _currentSelectedIndex;
		}
		
		protected function addCellToContainer(cell:IListCell):void
		{
			cell.addEventListener(MouseEvent.CLICK,__onItemInteractive);
			cell.addEventListener(MouseEvent.MOUSE_DOWN,__onItemInteractive);
			cell.addEventListener(MouseEvent.MOUSE_UP,__onItemInteractive);
			cell.addEventListener(MouseEvent.ROLL_OVER,__onItemInteractive);
			cell.addEventListener(MouseEvent.ROLL_OUT,__onItemInteractive);
			cell.addEventListener(MouseEvent.DOUBLE_CLICK,__onItemInteractive);
			_cells.push(_cellsContainer.addChild(cell.asDisplayObject()));
		}
		public function set updateItemWidth(value : int) : void
		{
			for each(var item : Object in _cells)
			{
				item["updateItemWidth"] = value;
			}
		}
		
		override protected function addChildren():void
		{
			super.addChildren();
			addChild(_mouseActiveObjectShape);
			addChild(_maskShape);
			addChild(_cellsContainer);
		}
		protected function creatMaskShape():void
		{
			_maskShape = new Shape();
			_maskShape.graphics.beginFill(0xFF0000,1);
			_maskShape.graphics.drawRect(0,0,100,100);
			_maskShape.graphics.endFill();
			
			_mouseActiveObjectShape = new Shape();
			_mouseActiveObjectShape.graphics.beginFill(0xFF0000,0);
			_mouseActiveObjectShape.graphics.drawRect(0,0,100,100);
			_mouseActiveObjectShape.graphics.endFill();
		}
		
		protected function createCells():void{
			if(_factory.isShareCells()){
				createCellsWhenShareCells();
			}else{
				createCellsWhenNotShareCells();
			}
		}
		
		protected function createCellsWhenNotShareCells():void
		{
			
		}
		
		protected function createCellsWhenShareCells():void
		{
			var ih:int = _factory.getCellHeight();
			var needNum:int = Math.floor(_height/ih) + 2;
			_viewWidth = _factory.getViewWidthNoCount();
			if(_factory.isAllCellHasSameHeight())
			{
				_viewHeight = _model.getSize()*ih;
			}else
			{
				_viewHeight = _model.getAllCellHeight();
			}
			if(_cells.length == needNum) return;
			
			var i:int;
			var cell:IListCell;
			
			var maxCellWidth:int;
			//create needed
			if(_cells.length < needNum){
				var addNum:int = needNum - _cells.length;
				for(i=0; i<addNum; i++){
					cell = createNewCell();
					maxCellWidth = Math.max(cell.width,maxCellWidth);
					addCellToContainer(cell);
				}
			}else if(_cells.length > needNum){ //remove mored
				var removeIndex:int = needNum;
				var removed:Vector.<IListCell> = _cells.splice(removeIndex, _cells.length-removeIndex);
				for(i=0; i<removed.length; i++){
					removeCellFromContainer(removed[i]);
				}
			}
		}
		
		protected function createNewCell():IListCell
		{
			if(_factory == null)return null;
			return _factory.createNewCell();
		}
		
		protected function fireStateChanged(programmatic:Boolean=true):void{
			dispatchEvent(new InteractiveEvent(InteractiveEvent.STATE_CHANGED));
		}
		
		protected function getListCellModelHeight(index:int):int
		{
//			TODO
			return 0
		}
		
		protected function getViewMaxPos():IntPoint
		{
			var showSize:IntDimension = getExtentSize();
			var viewSize:IntDimension = getViewSize();
			var p:IntPoint = new IntPoint(viewSize.width-showSize.width, viewSize.height-showSize.height);
			if(p.x < 0) p.x = 0;
			if(p.y < 0) p.y = 0;
			return p;
		}
		
		override protected function init():void
		{
			creatMaskShape();
			_cellsContainer = new Sprite();
			addChild(_cellsContainer);
			_viewPosition = new IntPoint(0, 0);
			_firstVisibleIndex = 0;
			_lastVisibleIndex = -1;
			_firstVisibleIndexOffset = 0;
			_lastVisibleIndexOffset = 0;
			_visibleRowCount = -1;
			_visibleCellWidth = -1;
			_cells = new Vector.<IListCell>();
			super.init();
			_model = new VectorListModel();
			_model.addListDataListener(this);
		}
		
		protected function layoutWhenShareCellsHasNotSameHeight():void
		{
			createCellsWhenShareCells();
			restrictionViewPos(_viewPosition);
			var x:int = _viewPosition.x;
			var y:int = _viewPosition.y;
			var startIndex:int = _model.getStartIndexByPosY(y);
			var listSize:int = _model.getSize();
			var cx:int = - x;
			var maxY:int = _height;
			if(listSize < 0){
				_lastVisibleIndex = -1;
			}
			for(var i:int = 0; i<_cells.length; i++){
				var cell:IListCell = _cells[i];
				var ldIndex:int = startIndex + i;
				if(ldIndex < listSize){
					cell.setCellValue(_model.getElementAt(ldIndex));
					cell.setListCellStatus(this, isSelectedIndex(ldIndex), ldIndex);
					cell.visible = true;
					cell.x = cx;
					cell.y = _model.getCellPosFromIndex(ldIndex) - y;
					if(cell.y < maxY){
						_lastVisibleIndex = ldIndex;
					}
				}else{
					cell.visible = false;
				}
			}
			_firstVisibleIndex = startIndex;
		}
		
		protected function layoutWhenShareCellsHasSameHeight():void
		{
			createCellsWhenShareCells();
			restrictionViewPos(_viewPosition);
			var x:int = _viewPosition.x;
			var y:int = _viewPosition.y;
			var ih:int = _factory.getCellHeight();
			var startIndex:int = Math.floor(y/ih);
			var startY:int = startIndex*ih - y;
			var listSize:int = _model.getSize();
			var cx:int = - x;
			var cy:int = startY;
			var maxY:int = _height;
			if(listSize < 0){
				_lastVisibleIndex = -1;
			}
			for(var i:int = 0; i<_cells.length; i++){
				var cell:IListCell = _cells[i];
				var ldIndex:int = startIndex + i;
				if(ldIndex < listSize){
					cell.setCellValue(_model.getElementAt(ldIndex));
					cell.setListCellStatus(this, isSelectedIndex(ldIndex), ldIndex);
					cell.visible = true;
					cell.x = cx;
					cell.y = cy;
					if(cy < maxY){
						_lastVisibleIndex = ldIndex;
					}
					cy += ih;
				}else{
					cell.visible = false;
				}
			}
			_firstVisibleIndex = startIndex;
		}
		
		override protected function onProppertiesUpdate():void
		{
			super.onProppertiesUpdate();
			var mustUpdateListView:Boolean = false;
			_cellsContainer.mask = _maskShape;
			if(_changedPropeties[P_model] || _changedPropeties[P_cellFactory] || _changedPropeties[P_viewPosition]
				|| _changedPropeties[Component.P_width] || _changedPropeties[Component.P_height])
			{
				if(_changedPropeties[P_cellFactory])removeAllCell();
				mustUpdateListView = true;
			}
			
			if(mustUpdateListView)
			{
				updateListView();
			}
			
			if(_changedPropeties[P_verticalBlockIncrement] || _changedPropeties[P_verticalUnitIncrement]
				|| _changedPropeties[P_horizontalBlockIncrement] || _changedPropeties[P_horizontalUnitIncrement]
				|| _changedPropeties[Component.P_height] || _changedPropeties[Component.P_width] || _changedPropeties[P_viewPosition]
				|| _changedPropeties[P_viewSize])
			{
				fireStateChanged();
			}
		}
		
		protected function refreshViewSize():void
		{
			if(_factory.isShareCells()){
				_viewWidth = _factory.getViewWidthNoCount();
				if(_factory.isAllCellHasSameHeight())
				{
					_viewHeight = _model.getSize()*_factory.getCellHeight();
				}
				else
				{
					_viewHeight = _model.getAllCellHeight();
				}
			}
		}
		
		protected function removeAllCell():void
		{
			for(var i:int = 0;i<_cells.length;i++)
			{
				removeCellFromContainer(_cells[i]);
			}
			_cells = new Vector.<IListCell>();
		}
		
		public function set defaultSelectItemByCellValue(value:*) : void
		{
			var index:int = _model.indexOf(value);
			var targetCell:IListCell = getItemByValue(value);
			if(targetCell == null)
			{
				scrollToIndex(index);
				targetCell = getItemByValue(value);
			}
			if(targetCell != null)
			{
				_currentSelectedIndex = index;
				targetCell.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
				return;
			}
			dispatchEvent(new ListItemEvent(null,null,ListItemEvent.LIST_ITEM_CLICK,-1));
		}
		
		public function getItemByValue(value:Object):IListCell
		{
			for each(var item : IListCell in _cells)
			{
				if(item.getCellValue() == value)
				{
					return item;
				}
			}
			return null;
		}
		
		protected function removeCellFromContainer(cell:IListCell):void
		{
			cell.removeEventListener(MouseEvent.CLICK,__onItemInteractive);
			cell.removeEventListener(MouseEvent.MOUSE_DOWN,__onItemInteractive);
			cell.removeEventListener(MouseEvent.MOUSE_UP,__onItemInteractive);
			cell.removeEventListener(MouseEvent.ROLL_OVER,__onItemInteractive);
			cell.removeEventListener(MouseEvent.ROLL_OUT,__onItemInteractive);
			cell.removeEventListener(MouseEvent.DOUBLE_CLICK,__onItemInteractive);
			ObjectUtils.disposeObject(cell);
		}
		
		protected function restrictionViewPos(p:IntPoint):IntPoint
		{
			var maxPos:IntPoint = getViewMaxPos();
			p.x = Math.max(0, Math.min(maxPos.x, p.x));
			p.y = Math.max(0, Math.min(maxPos.y, p.y));
			return p;
		}
		
		protected function updatePos():void
		{
			if(_factory.isShareCells()){
				if(_factory.isAllCellHasSameHeight())
				{
					layoutWhenShareCellsHasSameHeight();
				}else
				{
					layoutWhenShareCellsHasNotSameHeight();
				}
			}
		}
		
		protected function updateShowMask():void
		{
			_mouseActiveObjectShape.width = _maskShape.width = _width;
			_mouseActiveObjectShape.height = _maskShape.height = _height;
		}
		
		public function get viewHeight():Number
		{
			return _viewHeight;
		}
	}
}